利用htmlunit和jsoup来实现爬取js的动态网页实践(执行js) - CSDN博客

创建时间:2018/3/2 14:46
来源:http://blog.csdn.net/anla_/article/details/50199815


2015年12月06日 23:41:50 18281人阅读 评论(2) 收藏 举报
.
分类:
网络爬虫学习(6)
.

版权声明:本博客所有的原创文章,转载请注明出处,作者皆保留版权。

更新,这就尴尬了,这篇文章博客阅读文章最多,但是被踩得也最多。

爬取思路:

所谓动态,就是通过请求后台,可以动态的改变相应的html页面,页面并不是一开始就全部展现出来的。

大部分操作都是通过请求完成的,一次请求,一次返回。而在大多数网页中请求往往都被开发者隐藏在了js代码中。

所以爬取动态网页的思路就转化为找出相应的js代码,并且执行相应的js代码,从而能够通过java代码动态的改变页面。

而当页面能够正确显示出来,我们也就可以类似于爬取静态网页般去爬取数据啦!



首先,可以利用htmlunit来模拟鼠标点击事件,这个容易实现:

  1. /** 
  2.  * 通过htmlunit来获得一些搜狗的网址。 
  3.  * 通过模拟鼠标点击事件来实现 
  4.  * @param key 
  5.  * @return 
  6.  * @throws Exception 
  7.  */  
  8. public String getNextUrl(String key){  
  9.     String page = new String();  
  10.     try {  
  11.         WebClient webClient = new WebClient();  
  12.         webClient.getOptions().setCssEnabled(false);  
  13.         webClient.getOptions().setJavaScriptEnabled(false);  
  14.         //去拿网页  
  15.         HtmlPage htmlPage = webClient.getPage("http://pic.sogou.com/");  
  16.         //得到表单  
  17.         HtmlForm form = htmlPage.getFormByName("searchForm");  
  18.         //得到提交按钮  
  19.         HtmlSubmitInput button = form.getInputByValue("搜狗搜索");  
  20.         //得到输入框  
  21.         HtmlTextInput textField = form.getInputByName("query");  
  22.         //输入内容  
  23.         textField.setValueAttribute(key);  
  24.         //点一下按钮  
  25.         HtmlPage nextPage = button.click();  
  26.         String str = nextPage.toString();  
  27.         page = cutString(str);  
  28.         webClient.close();  
  29.     } catch (Exception e) {  
  30.         e.printStackTrace();  
  31.     }   
  32.     return page;  
  33. }  

如上图所示,我就是通过java代码,向搜索矿中填入关键字,然后在通过getInputByValue方法获得button控件,最后直接button.click(),

即可以模拟点击,并且把点击后的返回的http请求解析到htmlpage中。




这个功能其实很强大,比如你可以通过这个功能去模拟抢票,或者利用点击事件,加上搜索相关知识,将某一整个系统离线下来,并且以html的形式保存。


接下来就是利用强大的htmlunit来执行js代码的过程了。

首先写一个简单的jsp页面:

  1. <%@ page language="java" contentType="text/html; charset=UTF-8"  
  2.     pageEncoding="UTF-8"%>  
  3. <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">  
  4. <html>  
  5. <head>  
  6. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">  
  7. <title>Insert title here</title>  
  8.   
  9. </head>  
  10.   
  11. <body id="body">  
  12.   
  13.     <label id="test">原数字</label>  
  14. </body>  
  15. <script type="text/javascript">  
  16.     function change(value) {  
  17.         document.getElementById("test").innerHTML = value;  
  18.         return "hello htmlUnit";  
  19.     }  
  20. </script>  
  21. </html>  
由上可知,jsp页面很简单,就一个函数change,用于给htmlUnit调用。

再下来就是一个使用htmlunit的类。该类通过支持JavaScript解释器,

然后在页面中嵌入自己写的JavaScript代码从而执行,并且获得执行后的返回结果以及返回页面。

  1. package com.blog.anla;  
  2.   
  3. import com.gargoylesoftware.htmlunit.ScriptResult;  
  4. import com.gargoylesoftware.htmlunit.WebClient;  
  5. import com.gargoylesoftware.htmlunit.html.HtmlPage;  
  6.   
  7. public class TestMyOwnPage {  
  8.     private void action() {  
  9.         WebClient webClient = new WebClient();  
  10.         try {  
  11.   
  12.             webClient.getOptions().setCssEnabled(false);  
  13.             webClient.getOptions().setJavaScriptEnabled(true); // 设置支持JavaScript。  
  14.             // 去拿网页  
  15.             HtmlPage htmlPage = webClient  
  16.                     .getPage("http://localhost:8989/testHtmlScrop/index.jsp");  
  17.   
  18.             String s = "更改后的数字";  
  19.             ScriptResult t = htmlPage.executeJavaScript("change(\"" + s  
  20.                     + "\");""injected script"500);  
  21.             // 这里是在500行插入这一小行JavaScript代码段,因为如果在默认的1行,那不会有结果  
  22.             // 因为js是顺序执行的,当你执行第一行js代码时,可能还没有渲染body里面的标签。  
  23.             HtmlPage myPage = (HtmlPage) t.getNewPage();  
  24.             String nextPage = myPage.asXml().toString();  
  25.             String nextPage2 = myPage.asText().toString();  
  26.         } catch (Exception e) {  
  27.             e.printStackTrace();  
  28.         } finally {  
  29.             webClient.close();  
  30.         }  
  31.   
  32.     }  
  33.   
  34.     public static void main(String[] args) {  
  35.         TestMyOwnPage tmop = new TestMyOwnPage();  
  36.         tmop.action();  
  37.     }  
  38. }  





在t.getNewPage()中有两个属性,一个是
javaScriptResult:执行该段代码后返回的结果,如果有(如上我写的,就返回hello htmlunit),如果没有(返回Undefined)。

newPage_:执行该段代码后返回的整个页面。

结果如图:

最终这段代码执行的结果如下:

asXml():将整个页面的html代码返回给我们:


而asText()则仅仅返回页面上能显示的值,即head和label标签:

这样的执行思路也就能够去动态的执行相应的js代码,从而爬取到需要的数据了。

----------------------------------------------------------------------------------------2017年7月更新--------------------------------------------------------------------------------------------------------

这两天在做一个有关于网络爬虫的系统

但是呢,一开始爬的时候就发现问题,js的动态页面的爬不下来

网上找了好多方法,google也问了,主要还是提到htmlunit,以下是核心代码,

使用htmlunit主要就是为了模拟浏览器操作,因为有些链接点击无法直接通过src来获得url,而通常使用JavaScript

进行简单拼接后的网址,所以这样一来,使用htmlunit直接来模拟浏览器点击,相比来说就更加的简单了。

  1.        WebClient webClient = new WebClient();    
  2.        webClient.getOptions().setJavaScriptEnabled(true); //启用JS解释器,默认为true    
  3.        webClient.getOptions().setCssEnabled(false); //禁用css支持        
  4.        webClient.getOptions().setThrowExceptionOnScriptError(false); //js运行错误时,是否抛出异常     
  5.        webClient.getOptions().setTimeout(20000);        
  6.        HtmlPage page = wc.getPage("http://www.hao123.com");    
  7. //我认为这个最重要  
  8.        String pageXml = page.asXml(); //以xml的形式获取响应文本    
  9.    
  10.        /**jsoup解析文档*/    
  11.        Document doc = Jsoup.parse(pageXml, "http://cq.qq.com");     

这个时候,就可以得到jsoup中的document对象了,接下来就好写了,就像爬普通静态网页一样了。
不过,webclient解析是还是会出现一些问题,js的问题,
主要是由于目标url的js写的有些问题,但在实际的浏览器中却会忽略,eclipse中会报异常。

今天一看,好多人踩啊哈,可能当时并没有认真的写博客吧,如果大家想找一个java爬虫的项目,可以去我的专栏

图片搜索  包括使用jsoup来爬图,以及lire来建立索引以及搜索图片。


赠人玫瑰手留余香,有问题的大家可以多多讨论呀!